package com.ccnu.demojava.qtpmessage;

import io.netty.buffer.CompositeByteBuf;

import java.nio.ByteOrder;

public class ByteUtil {
    public static byte getByte(byte[] bytes, int index) {
        assert index < bytes.length;
        return bytes[index];
    }

    protected static short getShort(byte[] bytes, int offset, ByteOrder byteOrder) {
        if (offset + 2 > bytes.length) {
            return getByte(bytes, offset);
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            return (short) ((getByte(bytes, offset) & 0xff) << 8 | getByte(bytes, offset + 1) & 0xff);
        } else {
            return (short) (getByte(bytes, offset) & 0xff | (getByte(bytes, offset + 1) & 0xff) << 8);
        }
    }

    protected static short getShortLE(byte[] bytes, int offset, ByteOrder byteOrder) {
        if (offset + 2 > bytes.length) {
            return getByte(bytes, offset);
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            return (short) (getByte(bytes, offset) & 0xff | (getByte(bytes, offset + 1) & 0xff) << 8);
        } else {
            return (short) ((getByte(bytes, offset) & 0xff) << 8 | getByte(bytes, offset + 1) & 0xff);
        }
    }

    public static int getUnsignedShort(byte[] bytes, int offset, ByteOrder byteOrder) {
        return getShort(bytes, offset, byteOrder) & 0xFFFF;
    }

    public static int getUnsignedShortLE(byte[] bytes, int offset, ByteOrder byteOrder) {
        return getShortLE(bytes, offset, byteOrder) & 0xFFFF;
    }
    public static long getUnsignedInt(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        return getInt(bytes, offset, byteOrder) & 0xFFFFFFFFL;
    }

    public static long getUnsignedIntLE(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        return getIntLE(bytes, offset, byteOrder) & 0xFFFFFFFFL;
    }

    protected static int getInt(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        if (offset + 4 > bytes.length) {
            throw new Exception("The parameter is incorrect.");
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            return (getShort(bytes, offset, byteOrder) & 0xffff) << 16 | getShort(bytes, offset + 2, byteOrder) & 0xffff;
        } else {
            return getShort(bytes, offset, byteOrder) & 0xFFFF | (getShort(bytes, offset + 2, byteOrder) & 0xFFFF) << 16;
        }
    }

    protected static int getIntLE(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        if (offset + 4 > bytes.length) {
            throw new Exception("The parameter is incorrect.");
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            return getShortLE(bytes, offset, byteOrder) & 0xffff | (getShortLE(bytes, offset + 2, byteOrder) & 0xffff) << 16;
        } else {
            return (getShortLE(bytes, offset, byteOrder) & 0xffff) << 16 | getShortLE(bytes, offset + 2, byteOrder) & 0xffff;
        }
    }

    protected static long getLong(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        if (offset + 8 > bytes.length) {
            throw new Exception("The parameter is incorrect.");
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            return (getInt(bytes, offset, byteOrder) & 0xffffffffL) << 32 | getInt(bytes, offset + 4, byteOrder) & 0xffffffffL;
        } else {
            return getInt(bytes, offset, byteOrder) & 0xFFFFFFFFL | (getInt(bytes, offset + 4, byteOrder) & 0xFFFFFFFFL) << 32;
        }
    }

    protected static long getLongLE(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        if (offset + 8 > bytes.length) {
            throw new Exception("The parameter is incorrect.");
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            return getIntLE(bytes, offset, byteOrder) & 0xffffffffL | (getIntLE(bytes, offset + 4, byteOrder) & 0xffffffffL) << 32;
        } else {
            return (getIntLE(bytes, offset, byteOrder) & 0xffffffffL) << 32 | getIntLE(bytes, offset + 4, byteOrder) & 0xffffffffL;
        }
    }

    public static float getFloat(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        return Float.intBitsToFloat(getInt(bytes, offset, byteOrder));
    }

    public static double getDouble(byte[] bytes, int offset, ByteOrder byteOrder) throws Exception {
        return Double.longBitsToDouble(getLong(bytes, offset, byteOrder));
    }

    public static boolean setByte(byte[] bytes, int offset, int data) {
        bytes[offset] = (byte)data;
        return true;
    }

    public static boolean setShort(byte[] bytes, int offset, int value, ByteOrder byteOrder) {
        if (offset + 2 > bytes.length) {
            return false;
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            setByte(bytes, offset, (byte) (value >>> 8));
            setByte(bytes, offset + 1, (byte) value);
        } else {
            setByte(bytes, offset, (byte) value);
            setByte(bytes, offset + 1, (byte) (value >>> 8));
        }
        return true;
    }

    protected static boolean setShortLE(byte[] bytes, int offset, int value, ByteOrder byteOrder) {
        if (offset + 2 > bytes.length) {
            return false;
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            setByte(bytes, offset, (byte) value);
            setByte(bytes, offset + 1, (byte) (value >>> 8));
        } else {
            setByte(bytes, offset, (byte) (value >>> 8));
            setByte(bytes, offset + 1, (byte) value);
        }
        return true;
    }

    public static boolean setInt(byte[] bytes, int offset, long value, ByteOrder byteOrder) {
        if (offset + 4 > bytes.length) {
            return false;
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            setShort(bytes, offset, (short) (value >>> 16), byteOrder);
            setShort(bytes, offset + 2, (short) value, byteOrder);
        } else {
            setShort(bytes, offset, (short) value, byteOrder);
            setShort(bytes, offset + 2, (short) (value >>> 16), byteOrder);
        }
        return true;
    }

    public static boolean setIntLE(byte[] bytes, int offset, int value, ByteOrder byteOrder) {
        if (offset + 4 > bytes.length) {
            return false;
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            setShortLE(bytes, offset, (short) value, byteOrder);
            setShortLE(bytes, offset + 2, (short) (value >>> 16), byteOrder);
        } else {
            setShortLE(bytes, offset, (short) (value >>> 16), byteOrder);
            setShortLE(bytes, offset + 2, (short) value, byteOrder);
        }
        return true;
    }

    public static boolean setLong(byte[] bytes, int offset, long value, ByteOrder byteOrder) {
        if (offset + 8 > bytes.length) {
            return false;
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            setInt(bytes, offset, (int) (value >>> 32), byteOrder);
            setInt(bytes, offset + 4, (int) value, byteOrder);
        } else {
            setInt(bytes, offset, (int) value, byteOrder);
            setInt(bytes, offset + 4, (int) (value >>> 32), byteOrder);
        }
        return true;
    }

    public static boolean setLongLE(byte[] bytes, int offset, long value, ByteOrder byteOrder) {
        if (offset + 8 > bytes.length) {
            return false;
        } else if (byteOrder == ByteOrder.BIG_ENDIAN) {
            setIntLE(bytes, offset, (int) value, byteOrder);
            setIntLE(bytes, offset + 4, (int) (value >>> 32), byteOrder);
        } else {
            setIntLE(bytes, offset, (int) (value >>> 32), byteOrder);
            setIntLE(bytes, offset + 4, (int) value, byteOrder);
        }
        return true;
    }

    public static byte[] long2Bytes(long data, ByteOrder byteOrder) {
        byte[] b = new byte[8];
        setLong(b, 0, data, byteOrder);
        return b;
    }
}
